/**
 *
 */
#include "RuleManager.hpp"

#include <QtCore/QAbstractListModel>
#include <QtCore/QPoint>

#include <QtGui/QAction>
#include <QtGui/QGridLayout>
#include <QtGui/QHBoxLayout>
#include <QtGui/QLabel>
#include <QtGui/QListView>
#include <QtGui/QMenu>
#include <QtGui/QPushButton>
#include <QtGui/QStandardItemModel>

// ========================================================================== //

RuleManager::RuleManager( const RuleData& rules, QWidget* parent )
    : QDialog( parent )
    , Rules( rules )
    , BirthConditions( 0 )
    , SurvivalConditions( 0 )
{
    setupGui();

    connect( BirthConditions, SIGNAL( customContextMenuRequested( QPoint ) ),
             this, SLOT( showBirthContextMenu( QPoint ) ) );

    connect( SurvivalConditions, SIGNAL( customContextMenuRequested( QPoint ) ),
             this, SLOT( showSurvivalContextMenu( QPoint ) ) );
}

RuleManager::~RuleManager()
{}

void RuleManager::showBirthContextMenu( const QPoint& pt )
{
    QModelIndex idx( BirthConditions -> indexAt( pt ) );
    QStandardItem* item = BirthModel -> itemFromIndex( idx );

    if ( item != 0 )
    {
        QMenu menu;
        QPoint globalPos( BirthConditions -> mapToGlobal( pt ) );

        QList< quint8 > possible;

        for ( quint8 i( 1u ); i < Rules.maxNeighbours() + 1u; ++i )
        {
            if ( ! Rules.birthConditions().contains( i ) )
                possible.append( i );
        }

        if ( possible.count() > 0 )
        {
            QMenu* addMenu = menu.addMenu( tr( "Add rule" ) );
            foreach ( quint8 i, possible )
            {
                QAction* addItem = new QAction( tr( "Number %0" ).arg( i ),
                                                this );
                addItem -> setData( i );

                connect( addItem, SIGNAL( triggered() ),
                         this, SLOT( addBirthCondition() ) );

                addMenu -> addAction( addItem );
            }
        }

        QAction* deleteItem = new QAction( tr( "Delete rule" ), this );
        deleteItem -> setData( idx.row() );

        connect( deleteItem, SIGNAL( triggered() ),
                 this, SLOT( deleteBirthCondition() ) );

        menu.addAction( deleteItem );

        menu.exec( globalPos );
    }
}

void RuleManager::addBirthCondition()
{
    QAction* sender = qobject_cast< QAction* >( QObject::sender() );

    if ( sender == 0 )
        return;

    disconnect( sender, SIGNAL( triggered() ),
                this, SLOT( addBirthCondition() ) );

    quint8 value( sender -> data().toInt() );

    if ( Rules.addBirthCondition( value ) )
    {
        int loc( Rules.birthConditions().indexOf( value ) );
        QStandardItem *item =
                new QStandardItem( tr( "A cell is born if %0 neighbours are alive" ).arg( value ) );
        BirthModel -> insertRow( loc, item );
    }
}

void RuleManager::deleteBirthCondition()
{
    QAction* sender = qobject_cast< QAction* >( QObject::sender() );

    if ( sender == 0 )
        return;

    disconnect( sender, SIGNAL( triggered() ),
                this, SLOT( deleteBirthCondition() ) );

    if ( BirthModel -> removeRow( sender -> data().toInt() ) )
    {
        QList< quint8 > tmp( Rules.birthConditions() );
        tmp.removeAt( sender -> data().toInt() );
        Rules.setBirthConditions( tmp );
    }
}

void RuleManager::showSurvivalContextMenu( const QPoint& pt )
{
    QModelIndex idx( SurvivalConditions -> indexAt( pt ) );
    QStandardItem* item = SurvivalModel -> itemFromIndex( idx );

    if ( item != 0 )
    {
        QMenu menu;
        QPoint globalPos( SurvivalConditions -> mapToGlobal( pt ) );

        QList< quint8 > possible;

        for ( quint8 i( 1u ); i < Rules.maxNeighbours() + 1u; ++i )
        {
            if ( ! Rules.survivalConditions().contains( i ) )
                possible.append( i );
        }

        if ( possible.count() > 0 )
        {
            QMenu* addMenu = menu.addMenu( tr( "Add rule" ) );
            foreach ( quint8 i, possible )
            {
                QAction* addItem = new QAction( tr( "Number %0" ).arg( i ),
                                                this );
                addItem -> setData( i );

                connect( addItem, SIGNAL( triggered() ),
                         this, SLOT( addSurvivalCondition() ) );

                addMenu -> addAction( addItem );
            }
        }

        QAction* deleteItem = new QAction( tr( "Delete rule" ), this );
        deleteItem -> setData( idx.row() );

        connect( deleteItem, SIGNAL( triggered() ),
                 this, SLOT( deleteSurvivalCondition() ) );

        menu.addAction( deleteItem );

        menu.exec( globalPos );
    }
}

void RuleManager::addSurvivalCondition()
{
    QAction* sender = qobject_cast< QAction* >( QObject::sender() );

    if ( sender == 0 )
        return;

    disconnect( sender, SIGNAL( triggered() ),
                this, SLOT( addSurvivalCondition() ) );

    quint8 value( sender -> data().toInt() );

    if ( Rules.addSurvivalCondition( value ) )
    {
        int loc( Rules.survivalConditions().indexOf( value ) );
        QStandardItem *item =
                new QStandardItem( tr( "A cell survives if %0 neighbours are alive" ).arg( value ) );
        SurvivalModel -> insertRow( loc, item );
    }
}

void RuleManager::deleteSurvivalCondition()
{
    QAction* sender = qobject_cast< QAction* >( QObject::sender() );

    if ( sender == 0 )
        return;

    disconnect( sender, SIGNAL( triggered() ),
                this, SLOT( deleteSurvivalCondition() ) );

    if ( SurvivalModel -> removeRow( sender -> data().toInt() ) )
    {
        QList< quint8 > tmp( Rules.survivalConditions() );
        tmp.removeAt( sender -> data().toInt() );
        Rules.setSurvivalConditions( tmp );
    }
}

void RuleManager::setupGui()
{
    QGridLayout* mainLayout = new QGridLayout( this );

    setLayout( mainLayout );

    /**
     * The window layout consists of two views, showing the current birth and
     * survival conditions. Under each of those are buttons which allows to add
     * or delete a rule.
     */

    BirthConditions = new QListView( this );
    SurvivalConditions = new QListView( this );

    mainLayout -> addWidget( new QLabel( tr( "Birth conditions" ) ),
                             0, 0, 1, 1, Qt::AlignLeft );
    mainLayout -> addWidget( BirthConditions, 1, 0, 1, 2 );

    mainLayout -> addWidget( new QLabel( tr( "Survival conditions" ) ),
                             2, 0, 1, 1, Qt::AlignLeft );
    mainLayout -> addWidget( SurvivalConditions, 3, 0, 1, 2 );

    BirthModel = new QStandardItemModel( Rules.birthConditions().count(),
                                         1, this );
    SurvivalModel = new QStandardItemModel( Rules.survivalConditions().count(),
                                            1, this );

    int idx( 0 );

    foreach ( quint8 value, Rules.birthConditions() )
    {
        QStandardItem *item =
                new QStandardItem( tr( "A cell is born if %0 neighbours are alive" ).arg( value ) );
        BirthModel -> setItem( idx++, item );
    }

    idx = 0;

    foreach ( quint8 value, Rules.survivalConditions() )
    {
        QStandardItem *item =
                new QStandardItem( tr( "A cell survives is %0 neighbours are alive" ).arg( value ) );
        SurvivalModel -> setItem( idx++, item );
    }

    BirthConditions -> setModel( BirthModel );
    BirthConditions -> setContextMenuPolicy( Qt::CustomContextMenu );
    SurvivalConditions -> setModel( SurvivalModel );
    SurvivalConditions -> setContextMenuPolicy( Qt::CustomContextMenu );

    QHBoxLayout* buttonLayout = new QHBoxLayout();
    mainLayout -> addLayout(  buttonLayout, 4, 1, 1, 1, Qt::AlignRight );

    QPushButton* accept = new QPushButton( tr( "&OK" ), this );
    connect( accept, SIGNAL( clicked() ), this, SLOT( accept() ) );
    buttonLayout -> addWidget( accept, 1, Qt::AlignRight );

    QPushButton* cancel = new QPushButton( tr( "&Cancel" ), this );
    connect( cancel, SIGNAL( clicked() ), this, SLOT( reject() ) );
    buttonLayout -> addWidget( cancel, 1, Qt::AlignRight );
}
